#!/bin/bash

function func_debug() {
	if [[ "${DEBUG_ON}" != "0" ]]; then
		echo "DEBUG: ${1}"
	fi
}

function func_write_file() {
	FILE_COUNT=`find ${TEST_DIR} -type f 2> /dev/null | wc -l`
	if (( ${FILE_COUNT} > ${MAX_FILE_COUNT} )); then
		func_debug "There are too many files already, skip write."
		return 2
	fi

	# Write new file that small then ${MAX_SIZE}
	FILE_SIZE=`echo $(( ${RANDOM} % ${MAX_SIZE} ))`
	TEMP_FILE=`mktemp --dry-run --tmpdir=${TMP_DIR}/cache`
	dd if=/dev/urandom of=${TEMP_FILE} bs=${FILE_SIZE} count=1 2> /dev/null
	FILE_SUM=`${CHECKSUM} ${TEMP_FILE} | awk '{print $1}'`

	# Md5sum and sort new file.
	# Add lock before write.
	touch ${TMP_DIR}/lock/${FILE_SUM}.lock
	SUB_DIR=${TEST_DIR}/${FILE_SUM:0:3}
	if [[ ! -d ${SUB_DIR} ]]; then
		mkdir ${SUB_DIR}
	fi
	mv ${TEMP_FILE} ${SUB_DIR}/${FILE_SUM}
	RET_CODE=$?

	# Remove lock after write.
	rm -f ${TMP_DIR}/lock/${FILE_SUM}.lock

	return ${RET_CODE}
}

function func_read_file() {
	# Find one of file.
	FILE_COUNT=`find ${TEST_DIR} -type f 2> /dev/null | wc -l`
	if (( ${FILE_COUNT} == 0 )); then
		func_debug "No file, skip."
		return 2
	fi
	FILE_READ_NUMBER=`echo $(( ${RANDOM} * ${RANDOM} % ${FILE_COUNT} + 1 ))`
	FILE_READ_NAME=`find ${TEST_DIR} -type f | head -n ${FILE_READ_NUMBER} \
		| tail -n 1` 2> /dev/null
	FILE_SHORT_NAME=`echo ${FILE_READ_NAME} | awk -F'/' '{print $NF}'`

	# Add lock before read.
	touch ${TMP_DIR}/lock/${FILE_SHORT_NAME}.lock
	# Skip file which will be removed.
	if [[ -f "${TMP_DIR}/lock/${FILE_SHORT_NAME}.remove" ]]; then
		func_debug "file ${FILE_SHORT_NAME} is deleting, skip."
		return 2
	fi

	# Check hash sum
	FILE_CHECK_SUM=`${CHECKSUM} ${FILE_READ_NAME} | awk '{print $1}'`
	if [[ "${FILE_SHORT_NAME}" == "${FILE_CHECK_SUM}" ]]; then
		#func_debug "Read file success."
		RET_CODE=0
	else
		#func_debug "Read file failed."
		RET_CODE=1
	fi
	# Remove lock after read.
	rm -f ${TMP_DIR}/lock/${FILE_SHORT_NAME}.lock

	return ${RET_CODE}
}

function func_remove_file() {
	# Find one of file.
	FILE_COUNT=`find ${TEST_DIR} -type f 2> /dev/null | wc -l`
	if (( ${FILE_COUNT} == 0 )); then
		func_debug "No file, skip."
		return 2
	fi
	FILE_READ_NUMBER=`echo $(( ${RANDOM} * ${RANDOM} % ${FILE_COUNT} + 1 ))`
	FILE_READ_NAME=`find ${TEST_DIR} -type f | head -n ${FILE_READ_NUMBER} \
		| tail -n 1` 2> /dev/null
	FILE_SHORT_NAME=`echo ${FILE_READ_NAME} | awk -F'/' '{print $NF}'`

	# Skip file which in use.
	sleep 0.01
	if [[ -f "${TMP_DIR}/lock/${FILE_SHORT_NAME}.remove" ]]; then
		func_debug "file ${FILE_SHORT_NAME} is deleting, skip."
		return 2
	elif [[ -f "${TMP_DIR}/lock/${FILE_SHORT_NAME}.lock" ]]; then
		func_debug "file ${FILE_SHORT_NAME} is in use, skip."
		return 2
	fi
	# Mark file as delete.
	touch ${TMP_DIR}/lock/${FILE_SHORT_NAME}.remove
	
	rm -f ${FILE_READ_NAME}

	SUB_DIR=${TEST_DIR}/${FILE_SHORT_NAME:0:3}
	if [[ "`ls -l ${SUB_DIR} | wc -l`" == "0" ]]; then
		rmdir ${SUB_DIR}
	fi
	# rm the .remove file.
	touch ${TMP_DIR}/lock/${FILE_SHORT_NAME}.remove

	if [[ ! -f "${FILE_READ_NAME}" ]]; then
		return 0
	else
		return 1
	fi
}

function func_get_action() {
	TOTAL_WEIGHT=$(( ${WRITE_WEIGHT} + ${READ_WEIGHT} + ${REMOVE_WEIGHT} ))
	TEMP=$(( ${RANDOM} % ${TOTAL_WEIGHT} ))
	if (( ${TEMP} < ${WRITE_WEIGHT} )); then
		echo "WRITE"
	elif (( ${TEMP} < $(( ${WRITE_WEIGHT} + ${READ_WEIGHT} )) )); then
		echo "READ"
	else
		echo "REMOVE"
	fi
}

function func_process() {
	PROCESS_ID=${1}
	FILE_STATUS=${TMP_DIR}/process_${PROCESS_ID}.status
	WRITE_SUCCESS=0
	WRITE_FAILED=0
	WRITE_SKIP=0
	READ_SUCCESS=0
	READ_FAILED=0
	READ_SKIP=0
	REMOVE_SUCCESS=0
	REMOVE_FAILED=0
	REMOVE_SKIP=0

	while [[ ! -f ${TMP_DIR}/let_us_exit.status ]]
	do
		if [[ -f "${TMP_DIR}/config.tmp" ]]; then
			KEY_VALUE=`head -n 1 ${TMP_DIR}/config.tmp`
			export ${KEY_VALUE}
		fi
		ACTION=`func_get_action`
		if [[ "${ACTION}" == "WRITE" ]]; then
			func_write_file
			RET_CODE=$?
		elif [[ "${ACTION}" == "READ" ]]; then
			func_read_file
			RET_CODE=$?
		elif [[ "${ACTION}" == "REMOVE" ]]; then
			func_remove_file
			RET_CODE=$?
		fi
		if [[ ${RET_CODE} == "0" ]]; then
			RET_STATUS="SUCCESS"
		elif [[ ${RET_CODE} == "1" ]]; then
			RET_STATUS="FAILED"
		elif [[ ${RET_CODE} == "2" ]]; then
			RET_STATUS="SKIP"
		fi
		INDEX="${ACTION}_${RET_STATUS}"
		export ${INDEX}=`echo $(( ${!INDEX} + 1 ))`

		# Write current status to .status file.
		echo -e "== Process ID: ${PROCESS_ID}" \
			> ${FILE_STATUS}
		echo -e "Action\tSuccess\tFailed\tSkip" \
			>> ${FILE_STATUS}
		echo -e "Write\t${WRITE_SUCCESS}\t${WRITE_FAILED}\t${WRITE_SKIP}" \
			>> ${FILE_STATUS}
		echo -e "Read\t${READ_SUCCESS}\t${READ_FAILED}\t${READ_SKIP}" \
			>> ${FILE_STATUS}
		echo -e "Remove\t${REMOVE_SUCCESS}\t${REMOVE_FAILED}\t${REMOVE_SKIP}" \
			>> ${FILE_STATUS}

	done
	echo -e "stopped." >> ${FILE_STATUS}

	rm ${TMP_DIR}/process_${PROCESS_ID}.pid
}

function func_start() {
	if [[ "`find ${TMP_DIR} -maxdepth 1 -type f -name "process_*.pid" \
		| wc -l`" != "0" ]]; then
		echo "Process runnning, please use restart instead."
		return 0
	fi

	echo "starting"
	for I in `seq 1 ${PROCESS_NUMBER}`
	do
		func_process ${I} &
		echo $! > ${TMP_DIR}/process_${I}.pid
	done
}

function func_stop() {
	touch ${TMP_DIR}/let_us_exit.status
	echo "stoping..."
	sleep 3
	rm -f ${TMP_DIR}/let_us_exit.status
}

function func_status() {
	WRITE_SUCCESS_LAST=0
	READ_SUCCESS_LAST=0
	REMOVE_SUCCESS_LAST=0
	while [[ ! -f ${TMP_DIR}/got_quit_char.status ]]
	do
		# Refresh config.
		if [[ -f "${TMP_DIR}/config.tmp" ]]; then
			KEY_VALUE=`head -n 1 ${TMP_DIR}/config.tmp`
			export ${KEY_VALUE}
		fi

		# Initial.
		WRITE_SUCCESS=0
		WRITE_FAILED=0
		WRITE_SKIP=0
		READ_SUCCESS=0
		READ_FAILED=0
		READ_SKIP=0
		REMOVE_SUCCESS=0
		REMOVE_FAILED=0
		REMOVE_SKIP=0

		for ID in `seq 1 ${PROCESS_NUMBER}`
		do
			if [[ ! -f "${TMP_DIR}/process_${ID}.pid" ]]; then
				# Process ${ID} is not running.
				echo "Process ${ID} is not running."
				continue
			fi
			W_SU_CU=`grep -e "^Write" ${TMP_DIR}/process_${ID}.status \
				| awk '{print $2}'`
			W_FA_CU=`grep -e "^Write" ${TMP_DIR}/process_${ID}.status \
				| awk '{print $3}'`
			W_SK_CU=`grep -e "^Write" ${TMP_DIR}/process_${ID}.status \
				| awk '{print $4}'`
			R_SU_CU=`grep -e "^Read" ${TMP_DIR}/process_${ID}.status \
				| awk '{print $2}'`
			R_FA_CU=`grep -e "^Read" ${TMP_DIR}/process_${ID}.status \
				| awk '{print $3}'`
			R_SK_CU=`grep -e "^Read" ${TMP_DIR}/process_${ID}.status \
				| awk '{print $4}'`
			D_SU_CU=`grep -e "^Remove" ${TMP_DIR}/process_${ID}.status \
				| awk '{print $2}'`
			D_FA_CU=`grep -e "^Remove" ${TMP_DIR}/process_${ID}.status \
				| awk '{print $3}'`
			D_SK_CU=`grep -e "^Remove" ${TMP_DIR}/process_${ID}.status \
				| awk '{print $4}'`
			if [[ -z ${W_SU_CU} || -z ${W_FA_CU} || -z ${W_SK_CU} ]]; then
				continue
			elif [[ -z ${R_SU_CU} || -z ${R_FA_CU} || -z ${R_SK_CU} ]]; then
				continue
			elif [[ -z ${D_SU_CU} || -z ${D_FA_CU} || -z ${D_SK_CU} ]]; then
				continue
			fi
			WRITE_SUCCESS=$(( ${WRITE_SUCCESS} + ${W_SU_CU} ))
			WRITE_FAILED=$(( ${WRITE_FAILED} + ${W_FA_CU} ))
			WRITE_SKIP=$(( ${WRITE_SKIP} + ${W_SK_CU} ))
			READ_SUCCESS=$(( ${READ_SUCCESS} + ${R_SU_CU} ))
			READ_FAILED=$(( ${READ_FAILED} + ${R_FA_CU} ))
			READ_SKIP=$(( ${READ_SKIP} + ${R_SK_CU} ))
			REMOVE_SUCCESS=$(( ${REMOVE_SUCCESS} + ${D_SU_CU} ))
			REMOVE_FAILED=$(( ${REMOVE_FAILED} + ${D_FA_CU} ))
			REMOVE_SKIP=$(( ${REMOVE_SKIP} + ${D_SK_CU} ))
		done

		clear
		echo -e "Action\tSuccess\tFailed\tSkip"
		echo -e "Write\t${WRITE_SUCCESS}\t${WRITE_FAILED}\t${WRITE_SKIP}"
		echo -e "Read\t${READ_SUCCESS}\t${READ_FAILED}\t${READ_SKIP}"
		echo -e "Remove\t${REMOVE_SUCCESS}\t${REMOVE_FAILED}\t${REMOVE_SKIP}"

		W_SU_RATE=$(( $(( ${WRITE_SUCCESS} - ${WRITE_SUCCESS_LAST} )) \
			/ ${STATUS_REFRESH_TIMEOUT} ))
		R_SU_RATE=$(( $(( ${READ_SUCCESS} - ${READ_SUCCESS_LAST} )) \
			/ ${STATUS_REFRESH_TIMEOUT} ))
		D_SU_RATE=$(( $(( ${REMOVE_SUCCESS} - ${REMOVE_SUCCESS_LAST} )) \
			/ ${STATUS_REFRESH_TIMEOUT} ))
		W_FA_PERC=`echo ${WRITE_FAILED} ${WRITE_SUCCESS} | awk '{print $1/$2}'`
		R_FA_PERC=`echo ${READ_FAILED} ${READ_SUCCESS} | awk '{print $1/$2}'`
		D_FA_PERC=`echo ${REMOVE_FAILED} ${REMOVE_SUCCESS} | awk '{print $1/$2}'`
		echo -n -e "\n"
		echo -e "Action\tFile_Per_Second\t Failed_Percent"
		echo -e "Write\t${W_SU_RATE}fps\t${W_FA_PERC}"
		echo -e "Read\t${R_SU_RATE}fps\t${R_FA_PERC}"
		echo -e "Remove\t${D_SU_RATE}fps\t${D_FA_PERC}"

		WRITE_SUCCESS_LAST=${WRITE_SUCCESS}
		READ_SUCCESS_LAST=${READ_SUCCESS}
		REMOVE_SUCCESS_LAST=${REMOVE_SUCCESS}

		read -t ${STATUS_REFRESH_TIMEOUT} -n 1 CHAR
		if [[ "${CHAR}" == "q" ]]; then
			touch ${TMP_DIR}/got_quit_char.status
			echo -n -e "\n"
		fi
	done
	rm -f ${TMP_DIR}/got_quit_char.status
}

